/**********************************
	The Player Class that Defines
	What the player can do

	By Julian Williams
	Jan 22, 2010
***********************************/


#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>

#include <math.h>
#include <iostream>


#include "app.h"
#include "player.h"
#include "image_manager.h"
#include "sound_manager.h"
#include "objects.h"
#include "weapons.h"
#include "input.h"
#include "iniparser.h"
#include "helper.h"
#include "levels.h"
#include "collision.h"
#include "explosions.h"
#include "ScreenFade.h"
#include "Camera.h"



using namespace std;




Player player;


Player::Player()
{
	tile_size.x = 0;
	tile_size.y = 0;

	selected_weapon = 0;

	Set_State(PL_AIR);
	Set_Visability(true);

	speed = 10;
	max_speed = 2;
}



/* Load Player Image */


//School code!
//016471



//Get/Set Action
int Player::Get_Action() { return Action; }
void Player::Set_Action(int A) { Action = A; }





//Algorithm to kill the player
void Player::Kill(death_types types)
{
}//End ov function



    
/* Get player Properties from Charactor */
bool Player::Load_Character(std::string character_folder)
{
		
//grab and create the nessisary directories
	string image_location = character_folder + string("image.png");
	string deathSound_location = character_folder + string("death_sound.wav");
	string cfg_location = character_folder + string("properties.cfg");
	
	cout << "Character Config from: " << cfg_location << endl;
	cout << "Character Image from: " << image_location << endl;
	cout << "Character Death sound from: " << deathSound_location << "\n\n";


	//Grab the image and apply a color mask
		if(!Load_Image(image_location)) { return false; }



	//Grab and set image sound
		death_buf = gSoundBufferManager.getResource(deathSound_location);
		death_snd.SetBuffer(*death_buf);
	




//Grab Weapon Config
		dictionary	*	ini = iniparser_load(cfg_location.c_str());

		if (ini==NULL)
		{
			cout << "Couldn't load configuration file, sorry :P \n";
			return false;
		}

		//Get all of the properties
		name	= iniparser_getstring(ini, "player:name", "Default"); 
		speed   = iniparser_getint(ini, "player:acceleration_speed", 10);
		slow_rotation_speed = iniparser_getdouble(ini, "player:slow_rotation_speed", 2.0);
		fast_rotation_speed = iniparser_getdouble(ini, "player:fast_rotation_speed", 4.0);


		cout << "Name: " << name << endl;
		cout << "Speed: " << speed << endl;
		cout << "Rotation Speed: " << rotation_speed <<  endl;

	//Animation related information
	
		tile_size.x = iniparser_getint(ini, "animation:tile_width", 0);
		tile_size.y = iniparser_getint(ini, "animation:tile_height", 0);

		cout << "Tile Size.X: " << tile_size.x << endl;
		cout << "Tile Size.Y: " << tile_size.y << endl;

	//frame to play the 
		step_frame[0] =  iniparser_getint(ini, "animation:thrust_sound_frame_1" , 1);

		shoulder_point.x = iniparser_getint(ini, "animation:weapon_point_x", 18);
		shoulder_point.y = iniparser_getint(ini, "animation:weapon_point_y", 20);

		cout << "Shoulder Point.X: " << shoulder_point.x << endl;
		cout << "Shoulder Point.Y: " << shoulder_point.y << endl << endl;


		//Load in animation frames
		standFrames.start = iniparser_getint(ini, "animation:stand_start", 1);
		standFrames.end = iniparser_getint(ini, "animation:stand_end", 4);
		standFrames.speed = iniparser_getdouble(ini, "animation:stand_frame_speed", 1.0);

		cout << "standFrames Start: " << standFrames.start << ", standFrames End: " << standFrames.end << ", standFrames Speed: " << standFrames.speed << "\n\n";
		

		walkFrames.start = iniparser_getint(ini, "animation:walk_start", 1);
		walkFrames.end = iniparser_getint(ini, "animation:walk_end", 4);
		walkFrames.speed = iniparser_getdouble(ini, "animation:walk_frame_speed", 1.0);

		cout << "WalkFrames Start: " << walkFrames.start << ", walkFrames End: " << walkFrames.end << ", walkFrames Speed: " << walkFrames.speed << "\n\n";
		

		jumpFrames.start = iniparser_getint(ini, "animation:jump_start", 8);
		jumpFrames.end = iniparser_getint(ini, "animation:jump_end", 8);
		jumpFrames.speed = iniparser_getdouble(ini, "animation:jump_speed", 2.0);

		cout << "jumpFrames Start: " << jumpFrames.start << ", jumpFrames End: " << jumpFrames.end << ", jumpFrames Speed: " << jumpFrames.speed << "\n\n";
		cout << "**********************************************************\n\n";	


		//Temporary
		SetOrigin(GetImage()->GetWidth()/2, GetImage()->GetHeight()/2);


		//Load Exhaust particles
		//exhaustParticles.Load("resources/particles/");
		
//close cfg file
	//CFG_CloseFile(0);

	return true;
}//EoF





/* Update Animations */
/*********************/
void Player::Animation_Update()
{
	switch(Get_Action())
	{
		case PA_WALKING:

			counter += fabs(velocity.x * walkFrames.speed) * App::getInstance().Get_RenderWindow()->GetFrameTime(); //animate running according to speed and Delta Time

			  //make sure animation is within bounds
                    if(frame < walkFrames.start) { frame = walkFrames.start; }
					else if(frame > walkFrames.end) { frame = walkFrames.end; }
                             
                              
                   if(counter >= 3) 
				   {    
					   counter = 0;
                             
                             if(h_direction == DIR_RIGHT) 
							 {
								 if(velocity.x > 0)
								 {
                                    if(frame < walkFrames.end) { frame++; }
                                    else { frame = walkFrames.start; }  
								 }else{
									 if(frame > walkFrames.start) { frame--; }
                                    else { frame = walkFrames.end; } 
									}

							 }else if(h_direction == DIR_LEFT) {
								 if(velocity.x < 0)
								 {
									if(frame < walkFrames.end) { frame++; }
                                    else { frame = walkFrames.start; }  
					
								 }else{
									
									 if(frame > walkFrames.start) { frame--; }
                                    else { frame = walkFrames.end; }
								 }
							 }
                   }//counter check

				   break;

	

		case PA_STANDING:

			  counter += fabs(standFrames.speed) * App::getInstance().Get_RenderWindow()->GetFrameTime(); //animate running according to speed
                     
                   if(counter >= 3) 
				   {    
                             counter = 0;
							 if(frame < standFrames.start) { frame = standFrames.start; }
                             
									
                             if(frame < standFrames.end) { frame++; }
                             else { frame = standFrames.start; }  
				   }
				   //Temporary
				 //  frame = 0;
				   break;
		default:
			frame = 10;
			break;
	}//End of Switch!
								 


//Very cool mystery function
	//Updtaes which tile is displayed
	 Tile_Update();
   
}//EoF






//Draw the Player to the Screen
/********************************/
void Player::Draw( )
{
	 exhaustParticles.Draw();

	if(Get_Visability())
	{

		App::getInstance().Get_RenderWindow()->Draw(*this);
		App::getInstance().Get_RenderWindow()->Draw(guns[selected_weapon]); 
		App::getInstance().Get_RenderWindow()->Draw(guns[selected_weapon].Get_CursorSprite());

	
		
		


		if(guns[selected_weapon].Get_Frame() == guns[selected_weapon].shootFrame.start) 
		{
			App::getInstance().Get_RenderWindow()->Draw(guns[selected_weapon].muzzle_flash);
		}

	}//if visable
//Draw Player related Particles to the screen
		   
			Bullets.Draw();
			deathExplosion.Draw();
}//EoF





//Reset Player thingz
void Player::Reset()
{
	//restore position
	  Spawn();

	   

//Restart the death timer
	  death_timer = 300;

      velocity.x = 0;
	  velocity.y = 0;

	  Set_Angle(0);
 
	  Set_Visability(true);
	  Set_State(PL_AIR);

	  //deathExplosion.p_obj.clear();

	  //Fix position
	  
	  
}


//Player Spawn finder
/**********************/
bool Player::Spawn()
{

   
    for (int y = 0; y < Map.Get_BlockSize().y; y++)
	{
		for (int x = 0; x < Map.Get_BlockSize().x; x++) 
		{
       
        // block = MapGetBlock(x, y);
      //   cout << Map.GetMapBlock(x, y)->user1 << endl;

			 if (Map.GetMapBlock(x, y)->user1 == PLAYER_SPAWN)
			{
                          
                     
                                      //For X alignment
                                      if((x * Map.Get_TileSize().x) <= App::getInstance().Get_RenderWindow()->GetWidth()-412) 
                                      {
                                                       Map.start_pos.x = Map.Get_TileSize().x;
                                                       start_pos.x = ( x * Map.Get_TileSize().x ) - Map.Get_TileSize().x;
                                                      // cout << "X1 \n";
                                      }else if((x * Map.Get_TileSize().x )-412 >= Map.Get_PixelSize().x - App::getInstance().Get_RenderWindow()->GetWidth())
                                      {
                                                       Map.start_pos.x = Map.Get_PixelSize().x - (App::getInstance().Get_RenderWindow()->GetWidth() + Map.Get_TileSize().x);
                                                       start_pos.x = (x * Map.Get_TileSize().x) - Map.start_pos.x ;
		                                              // cout << "X2 \n";
                                      }else{
                                                       Map.start_pos.x = (x * Map.Get_TileSize().x) - 416;
                                                       start_pos.x = 416;

													 //  cout << Map.Get_TileSize().x<< ", " << App::getInstance().Get_RenderWindow()->GetWidth()-412 << endl;
													  // cout << "X3 \n";
                                      }
                                      
                                      
                                      
                                      //For Y alignment
                                      if((y * Map.Get_TileSize().y) <= App::getInstance().Get_RenderWindow()->GetHeight()-216) 
                                      {
                                                       Map.start_pos.y = Map.Get_TileSize().y;
                                                       start_pos.y = (y * Map.Get_TileSize().y) - Map.Get_TileSize().y;
													   cout << "Y1 \n";
                                      }else if((y * Map.Get_TileSize().y)-216 >= Map.Get_PixelSize().y - App::getInstance().Get_RenderWindow()->GetHeight())
                                      {
                                                       Map.start_pos.y = Map.Get_PixelSize().y  - (App::getInstance().Get_RenderWindow()->GetHeight() + Map.Get_TileSize().y);
                                                       start_pos.y = (y * Map.Get_TileSize().y) - Map.start_pos.y;
													   cout << "Y2 \n";
                                                       
                                      }else{
                                                       Map.start_pos.y = (y * Map.Get_TileSize().y) - 216;
                                                       start_pos.y = 216;
													   cout << "Y3 \n";
                                      }
			 }//if Spawn Tile
                                      
		}
	}//nested for loops  

	SetPosition(start_pos);
    Cam.Set_Camera(Map.start_pos);

    return true;                                    
}//End ov Function




void Player::Spawn_Exhaust(float x, float y)
{
	list< Particles >::iterator i;
	sf::Vector2f part_pos;

		exhaustParticles.Emit("Exhaust_2", sf::Vector2f(x, y), Get_Angle()-90, 0);
		/*
		for(i = exhaustParticles.Type.begin(); i != exhaustParticles.Type.end(); i++)
		{
			if(i->Get_Name() == string("Exhaust_2"))
			{
				part_pos.x = x;
				part_pos.y = y;

				exhaustParticles.Emit(*i, part_pos, Get_Angle() - 90, i->amountPerCycle);

				break;
			}
		}
		*/
}//EoF


void Player::Spawn_Exhaust(sf::Vector2f pos)
{
	Spawn_Exhaust(pos.x, pos.y);
}//EoF




//utility function to rotate weapon point
void Player::Rotate_WeaponPoint()
{
	current_sp = Collision::RotatePoint(current_sp, GetRotation());

}//EoF



//Handles player Movement
/************************/
void Player::Input_Check()
{    

	//if not dead, do thingz...
	if(Get_State() != PL_DEAD)
	{




	if(Binder.Test_Action("Shoot"))
	{
		if(guns[selected_weapon].Get_State() == WS_READY)
		{
			guns[selected_weapon].Set_State(WS_FIRING);
			guns[selected_weapon].s_fire.SetPitch(sf::Randomizer::Random(.9f, 1.01f));

		
					guns[selected_weapon].s_fire.Play();
			

			//Load through all the bullet types
			list< Particles >::iterator i;

			for(i = Bullets.Type.begin(); i != Bullets.Type.end(); i++)
			{
				
				//Load check to see if an ammo type is the same as the current guns'
				if(i->Get_Name() == guns[selected_weapon].Get_BulletName())
				{
					
					//cout << "Bullets Found yah!\n";


					//fire through all the bullets the gun shoots per shots
					for(int a = 0; a < guns[selected_weapon].Get_AmmoPerShot(); a++)
					{
						guns[selected_weapon].Spawn_Bullet(*i);
					}
					break;
				}
			}//for loop
		}//guns check
	}//Shoot Action

	//Create thrusters
	sf::Vector2f thrust;

	if(Binder.Test_Action("Right_Click"))
	{
		//cout << "happening";
		//Spawn_Exhaust(App::getInstance().Get_MousePosition());

		exhaustParticles.Emit("Exhaust_2", App::getInstance().Get_MousePosition(), sf::Vector2f(0, 0), 0, 0);
	}


	if(Binder.Test_Action("Speed_Rotate"))
	{
		rotation_speed = fast_rotation_speed;
	}else{
		rotation_speed = slow_rotation_speed;
	}

	

//Walk/Run function
	if(Binder.Test_Action("Thrust")) 
	{ 
		
		thrust.x = -Get_Speed() * cos(-1 * degreesToRadians(Get_Angle()-90));
		thrust.y = Get_Speed() * sin(degreesToRadians(Get_Angle()-90));
	

		Spawn_Exhaust(GetPosition().x, GetPosition().y + 20);

		//Add Thrust to Velocity
	}


	if(Binder.Test_Action("Move_Down"))
	{

		thrust.y = Get_Speed() * 0.6 ;
	}

	//rotation_speed = 2;
	if (Binder.Test_Action("Rotate_Left"))
     {    
		if((Get_Angle() >  260) || (angle < 20))
		{
			Rotate_Angle(rotation_speed * App::getInstance().Get_RenderWindow()->GetFrameTime());
			Rotate_WeaponPoint();
		}
				thrust.x = -Get_Speed()  * .5;
				Spawn_Exhaust(GetPosition().x, GetPosition().y + 20);
     }
	


	if (Binder.Test_Action("Rotate_Right"))
    {
		if((Get_Angle() < 100) || (angle > 340))
		{
				Rotate_Angle(-rotation_speed * App::getInstance().Get_RenderWindow()->GetFrameTime());
				Rotate_WeaponPoint();
		}

			thrust.x = Get_Speed() * .5;
			Spawn_Exhaust(GetPosition().x, GetPosition().y + 20);
	}

			//Apply Thrust to players velocity
			velocity.x += thrust.x * App::getInstance().Get_RenderWindow()->GetFrameTime();
			velocity.y += thrust.y * App::getInstance().Get_RenderWindow()->GetFrameTime();
					
	}//if not dead!


	//update Mechanics		
	Mechanics_Update();
	guns[selected_weapon].Mechanics_Update( ); //Update Weapon we are holding
		
}//EoF
     
     


/* Change Weapons */
void Player::Change_Weapon(int weapon_num)
{
	selected_weapon = weapon_num;
}



//Handel Map Collision
void Player::Map_Collision()
{
	sf::Vector2f check_pos[4];

	const float crash_dampener = 0.35;
//Save Position
	sf::Vector2f old_pos;
	old_pos.x = GetPosition().x;
	old_pos.y = GetPosition().y;


	//the 4 truth bools
    bool isColliding[4] = { false };
	const int radius = 20;


	//Set up collision test points
	check_pos[0].x = GetPosition().x - radius;
	check_pos[0].y = GetPosition().y - radius;

	check_pos[1].x = GetPosition().x + radius;
	check_pos[1].y = GetPosition().y - radius;

	check_pos[2].x = GetPosition().x - radius;
	check_pos[2].y = GetPosition().y + radius;

	check_pos[3].x = GetPosition().x + radius;
	check_pos[3].y = GetPosition().y + radius;



	//if on screen
	 if(Is_OnScreen())
	 {
		 for(int i = 0; i < 4; i++)
		 {
			if(Map.GetMapBlockInPixels(check_pos[i].x +  Cam.Get_Camera().x,  check_pos[i].y +  Cam.Get_Camera().y)->tl == true)
			{
				//SetPosition(GetPosition().x -velocity.x, GetPosition().y - velocity.y);

				isColliding[i] = true;
			}
			//cout << isColliding[i] << endl;
		 }
		 //cout << "*********************************\n";
// }//is on screen?

		// cout << "************************\n" << endl;

	 //test Left/Right side
	 if(((isColliding[0]) && (isColliding[2])) ||
	    ((isColliding[1]) && (isColliding[3])))
	 {


		 // velocity.x = -velocity.x * crash_dampener;
		  velocity.x = (abs(velocity.x) < 100) ? -velocity.x : -velocity.x * crash_dampener;
		  SetX(old_pos.x + velocity.x * App::getInstance().Get_RenderWindow()->GetFrameTime());


		//  p.Set_State(PS_DEAD);
	 //Top/Bottom side
	 }
	 
	 if(((isColliding[0]) && (isColliding[1])) ||
		((isColliding[2]) && (isColliding[3])))
	 {
		// p.Set_State(PS_DEAD);


		  velocity.y = (abs(velocity.y) < 100) ? -velocity.y : -velocity.y * crash_dampener;
		 SetY(old_pos.y + velocity.y * App::getInstance().Get_RenderWindow()->GetFrameTime());
		 
		 
			
	 //if on corner, then DIE
	 }

	 if((isColliding[0]) && (isColliding[1]) &&
			  (isColliding[2]) && (isColliding[3]))
	 {
		 Set_State(PS_DEAD);
	 }



	}//endof on screen
}//EoF


	

//Handles player Mechanics (gravity/velocity and the like)
/*********************************************************/     
void Player::Mechanics_Update()
{
	static float death_speed;

	//temporary
	 //Map.gravity = Map.savedGravity * App::getInstance().Get_RenderWindow()->GetFrameTime();


	//update info with angle
	//angle = GetRotation();

	
	switch(Get_State())
	{
		case PL_GROUND:		
			//velocity.x = 0;
			//velocity.y = 0;

			if(velocity.x != 0) 
			{ 
				Set_Action(PA_WALKING); 

			} else { 

				if( guns[selected_weapon].Get_State() == WS_READY )
				{
						Set_Action(PA_STANDING); 
				}else{
						Set_Action(PA_SHOOTING);
				}
			}
			 
			break;
		


		case PL_AIR:
			//Wall collision detection
				Map_Collision();

				velocity.y += Map.gravity;
				break;
		


		case PL_DEAD:




			//velocity.x = 0;
			//velocity.y = 0;
			Rotate_Angle(7);
			//if(velocity.y > 100) { velocity.y = 5; }

			death_speed = 40 * App::getInstance().Get_RenderWindow()->GetFrameTime();
			if(death_speed <= 0) { death_speed = 1; }

			//cout << death_speed << endl;
			death_timer -= death_speed;


			//Restart
			

			if((death_timer > 100 ) && (death_timer < 270))
			{

				list< Particles >::iterator i;
				//	cout << "Collision Detected!\n";

				//deathExplosion.Emit("Explosion", GetPosition());

				deathExplosion.Emit("Explosion", GetPosition(),  0, 0);

				/*
				for(i = deathExplosion.Type.begin(); i != deathExplosion.Type.end(); i++)
				{
					if(i->Get_Name() == string("Explosion"))
					{
						deathExplosion.Spawn(*i, GetPosition());
						
					}
				}
				*/
			}

			//set ship to invisable
			if(death_timer >= 299)
			{
				death_snd.Play();

			}else if(death_timer < 150)
			{
				Set_Visability(false);
			}//end of death_timer


			//Update particles
			deathExplosion.Update_Logic();


			if(death_timer <= 0)
			{
				ScreenFade::getInstance().Fade_Out();
				
					Map.Reset();
					Reset();
					
					guns[selected_weapon].Mechanics_Update( ); //Update Weapon we are holding

				ScreenFade::getInstance().Fade_In();
			}
			

			break;
	}//end ov switch

		//update spawned bullets
		Bullets.Update_Logic();

//Move the player and Animate him



		const int move_square = 50;

			//Scroll points;
		//These mark the bounderies for when the player stops moving on his own,
		//and starts scrolling the map
		const int scroll_left = (App::getInstance().Get_RenderWindow()->GetWidth()/2) - move_square;
		const int scroll_right = (App::getInstance().Get_RenderWindow()->GetWidth()/2) + move_square;
		const int scroll_up = (App::getInstance().Get_RenderWindow()->GetHeight()/2) - move_square;
		const int scroll_down = (App::getInstance().Get_RenderWindow()->GetHeight()/2) + move_square;
		
		//Player movement (temp)
		float scroll_x = Get_Velocity().x * App::getInstance().Get_RenderWindow()->GetFrameTime();
		float scroll_y = Get_Velocity().y * App::getInstance().Get_RenderWindow()->GetFrameTime();


		 //X cord
		 if((Get_Velocity().x < 0) && (GetPosition().x < scroll_left) && ( Cam.canScroll_Left())  ||
		 ((Get_Velocity().x > 0) && (GetPosition().x > scroll_right) && ( Cam.canScroll_Right()))) 
		 { 
			  Cam.Move_CameraX( scroll_x); 
			 // Parallax::Set_ScrollX(scroll_x);
			 //cout << "X->2 \n";

		 }else{
              SetX( GetPosition().x + velocity.x * App::getInstance().Get_RenderWindow()->GetFrameTime() );
			  Parallax::Set_ScrollX(0);
			  Cam.Set_ScrollX(0);
         }

		  


		 //Y cord
		 if( (Get_Velocity().y < 0) && (GetPosition().y < scroll_up) && ( Cam.canScroll_Up())  ||
		 ( (Get_Velocity().y > 0) && (GetPosition().y > scroll_down) && ( Cam.canScroll_Down()))) 
		 { 
			  Cam.Move_CameraY(scroll_y);
			  //Parallax::Set_ScrollY(scroll_y);
			// cout << "Y->2 \n";

		 }else{
             SetY(GetPosition().y + velocity.y * App::getInstance().Get_RenderWindow()->GetFrameTime());
			 Parallax::Set_ScrollY(0);
			 Cam.Set_ScrollY(0);
			// cout << "Y->3 \n";
         }
	


		 //Update Exhaust particles
		 exhaustParticles.Update_Logic();
		 

		 //Animate player
		 Animation_Update();

}//EoF
              
              
               












